package com.example.sudoku;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class CreateBoard {

    // ‘A’代表10 ‘B’代表11 ....... ‘G 代表16
    static char sixteen_number[]={'A','B','C','D','E','F','G'};
    // 标识矩阵 1为初始数据 0为待填入的数据
    public static int[][] memorize=new int[9][9];
    // 初始生成的棋盘
    public int[][] board;
    // 运行过程中的棋盘
    public static int[][] board2=new int[16][16];
    // 难度等级
    public int level;
    // 现有难度等级
    public int actualLevel;

    /*
        入口值：x,y代表棋盘坐标
        返回值：当前可填入的数据
        目的：返回在当前位置可填入的数据
      */
    public static int[] check(int x,int y) {
        int[] a=new int[10];
        for(int i=0;i<10;i++)
            a[i]=1;
        if(x < 0 || x > 8 || y < 0 || y > 8) return a;
        for(int i=0;i<9;i++) {
            int count1=board2[i][y];
            int count2=board2[x][i];
            a[count1]=0;
            a[count2]=0;
        }
        int count3=y/3;
        int count4=x/3;
        for(int i=0;i<3;i++) {
            for(int j=0;j<3;j++) {
                int count5=board2[count4*3+i][count3*3+j];
                a[count5]=0;
            }
        }
        if(memorize[x][y] == 1) {
            for(int i = 0; i < 10; i++) a[i] = 0;
        }
        return a;
    }

    // 判断提交的数独是否正确
    public static boolean submit_check(int x,int y) {
        int[] a=new int[10];
        for(int i=0;i<10;i++)
            a[i]=1;
        for(int i=0;i<9;i++) {
            if(i!=x)
            {
                int count1=board2[i][y];
                a[count1]=0;
            }
            if(i!=y)
            {
                int count2=board2[x][i];
                a[count2]=0;
            }
        }
        int count3=y/3;
        int count4=x/3;
        for(int i=0;i<3;i++) {
            for(int j=0;j<3;j++) {
                int count5=board2[count4*3+i][count3*3+j];
                if(((count4*3+i)!=x)&&((count3*3+j)!=y))
                    a[count5]=0;
            }
        }
        for(int i=1;i<10;i++)
        {
            if(a[i]==1)
                break;
            if(i==9&&a[i]==0) return false;
        }
        for(int i=1;i<10;i++)
            if(a[i]==1)
                if(board2[x][y]!=i)
                    return false;
        return true;
    }

    /*
        提交后核对是否为正确答案
        返回值：0正确答案，1答案不完整，2答案错误
    */
    public static int check_board() {
        for(int i=0;i<SaveBoard.return_size();i++)
            for(int j=0;j<SaveBoard.return_size();j++)
                if(!submit_check(i,j))
                    return 0;
        return 1;
    }

    /*
         入口值：x,y代表棋盘坐标，num代表想填入的值，flag=1代表判断此数是否可以填入，flag=0 不判断
         目的：在指定位置填入数据
    */
    public static void insertNum(int num, int x, int y, Boolean flag){
        if(!flag){
            if(memorize[x][y] == 0) board2[x][y] = num;
        }
        else {
            int [] a = check(x, y);
            if(a[num] == 1&&memorize[x][y]==0) board2[x][y] = num;
        }
    }

    /* 清空棋盘操作 */
    public static void clear_all() {
        for(int i=0;i<SaveBoard.return_size();i++)
            for(int j=0;j<SaveBoard.return_size();j++) {
                if(memorize[i][j]==0)
                    board2[i][j]=0;
            }
    }

    /* 清除指定位置填入数据 */
    public static void clear_one(int i,int j) {
        if(memorize[i][j]==0)
            board2[i][j]=0;
    }

    /* 打印棋盘(后端调试所用) */
    public static void printBoard(int[][] solutionBoard) {
        System.out.println("---------------------------------");
        for (int i=0;i<SaveBoard.return_size();i++) {
            for (int j=0;j<SaveBoard.return_size();j++) {
                int item=solutionBoard[i][j];
                if(item==0) {
                    System.out.print("_" + " ");
                }
                else if(item<10)
                    System.out.print(item + " ");
                else if(item>=10)
                    System.out.print(sixteen_number[item-10] + " ");
            }
            System.out.println("|");
        }
        System.out.println("---------------------------------");
    }

    /* 打印棋盘（仅供后端调试）*/
    public void printBoard() {
        printBoard(board);
    }

    /* 复制棋盘 */
    public void copy() {
        for(int i=0;i<SaveBoard.return_size();i++)
        {
            for(int j=0;j<SaveBoard.return_size();j++)
            {
                board2[j][i]=board[i][j];
            }
        }
    }

    /* 生成标识矩阵 */
    public static void copy_memorize() {
        for(int i=0;i<SaveBoard.return_size();i++)
            for(int j=0;j<SaveBoard.return_size();j++)
                if(board2[i][j]>=1&&board2[i][j]<=SaveBoard.return_size())
                    memorize[i][j]=1;
                else memorize[i][j]=0;
    }

    /* 返回标识矩阵 */
    public static int[][] return_memorize(){
        return memorize;
    }

    public void run(int level) {
        this.level = level;
        while(actualLevel != level) {
            initBoard();
            dig();
        }
        copy();
        copy_memorize();
    }

    // 根据初始局解出任意一个终局
    public void initBoard() {
        SudokuDLX sudoku = new SudokuDLX();

        randBoard();
        while(!sudoku.solve(board)) {
            randBoard();
        }
        SudokuDLX.copyBoard(sudoku.solutionBoard, board);
    }

    /*随机生成初始棋盘*/
    private void randBoard() {
        this.board = new int[SaveBoard.return_size()][SaveBoard.return_size()];
        int count = SaveBoard.return_init();
        Random random = new Random();
        while(count > 0) {
            // 随机生成棋盘x,y值
            int xRand = random.nextInt(SaveBoard.return_size());
            int yRand = random.nextInt(SaveBoard.return_size());
            // 随机生成数
            int value = random.nextInt(SaveBoard.return_size());

            if (board[xRand][yRand] == 0) {
                board[xRand][yRand] = value + 1;
                if (SudokuDLX.validateSudoku(board)) {
                    count--;
                } else {
                    board[xRand][yRand] = 0;
                }
            }
        }
    }

    /*挖洞法*/
    private void dig() {
        // 生成一个0~80的棋盘数组
        List<Integer> p1 = IntStream.range(0, SaveBoard.return_board()).boxed().collect(Collectors.toList());
        int count = level;

        Random random = new Random();
        SudokuDLX sudoku = new SudokuDLX();
        while(count > 0) {
            if (p1.isEmpty()) {
                break;
            } else {
                // 随机位置
                int target = random.nextInt(p1.size());
                int x = p1.get(target) / SaveBoard.return_size();
                int y = p1.get(target) % SaveBoard.return_size();
                int prevValue = board[x][y];
                p1.remove(target);
                boolean digble = true;
                // 若该位置填入其他数，也能的处正确解法，则不能保证唯一性，则该数不能删除，回溯
                for (int i : IntStream.rangeClosed(1, SaveBoard.return_size()).toArray()) {
                    if (i != prevValue) {
                        board[x][y] = i;
                        if (sudoku.solve(board)) {
                            digble = false;
                            break;
                        }
                    }
                }
                if (digble) {
                    board[x][y] = 0;
                    count--;
                } else {
                    board[x][y] = prevValue;
                }
            }
        }
        actualLevel = level - count;
    }
}
